#include "RR_Scheduler.h"
#include <vector>
#include <iostream>
#include <assert.h>

using namespace std;

RR_Scheduler::RR_Scheduler(int quantum)
{
	time_quantum = quantum;
}

RR_Scheduler::~RR_Scheduler()
{
}

void RR_Scheduler::setTimeQuantum(int quantum)
{
	time_quantum = quantum;
}


bool RR_Scheduler::compareStartTime(vector<Process>::iterator positionOne, vector<Process>::iterator positionTwo)
{
	//cout << "values: " << (*positionOne)->getStartTime() << "    " << (*positionTwo)->getStartTime();
	return (*positionOne).getArrivalTIme() > (*positionTwo).getArrivalTIme();
}

void RR_Scheduler::sortProcesses()
{
	vector<Process>::iterator end, positionOne, positionTwo;
	bool swapped = true;

	end = processList.end();

	do
	{
		swapped = false;
		positionTwo = ++processList.begin();
		for(positionOne = processList.begin(); positionTwo != end; positionOne++, positionTwo++)
		{
			if(compareStartTime(positionOne, positionTwo))
			{
				swap(*positionOne, *positionTwo);
				swapped = true;
			}
		}
	}while(swapped);
}

void RR_Scheduler::runScheduler()
{
	vector<Process>::iterator index, end, processListItr;
	vector<Process> ready_queue;
	int endTimeOfPrevProcess = 0;
	Process temp(0,0,0);

	//needed to ensure there wont be a problem with resource allocation of the vector
	//during run-time. 
	ready_queue.reserve(700);
	sortProcesses();

	processListItr = processList.begin();

	if(processListItr != processList.end())
	{
		if(processListItr == processList.begin())
			getQueuedProcesses(ready_queue, processListItr, (*processListItr).getArrivalTIme() + time_quantum);
		else
			getQueuedProcesses(ready_queue, processListItr, endTimeOfPrevProcess);
	}

	do
	{
		index = ready_queue.begin();
		end = ready_queue.end();
	
		if((*index).wasProcessed())
		{

			if((*index).getArrivalTIme() > endTimeOfPrevProcess)
			{
				if((*index).getCPU_Burst() - time_quantum <= 0)
				{
					(*index).setEndTime((*index).getStartTime() + (*index).getCPU_Burst());
					(*index).setFinished();
					assignValuesToProcessList(ready_queue.front());
				}
				else
				{
					(*index).setEndTime((*index).getArrivalTIme() + time_quantum);
					(*index).setCPU_Burst((*index).getCPU_Burst() - time_quantum);
					(*index).setProcessed();
					ready_queue.push_back(ready_queue.front());
				}

				endTimeOfPrevProcess = (*index).getEndTime();
				ready_queue.erase(index);
			}
			else
			{
				if((*index).getCPU_Burst() - time_quantum <= 0)
				{
					(*index).setEndTime(endTimeOfPrevProcess + (*index).getCPU_Burst());
					(*index).setFinished();
					assignValuesToProcessList(ready_queue.front());
				}
				else
				{
					(*index).setEndTime(endTimeOfPrevProcess + time_quantum);
					(*index).setCPU_Burst((*index).getCPU_Burst() - time_quantum);
					(*index).setProcessed();
					ready_queue.push_back(ready_queue.front());
					
				}

				endTimeOfPrevProcess = (*index).getEndTime();
				ready_queue.erase(index);
			}
		}
		else
		{
			if((*index).getArrivalTIme() > endTimeOfPrevProcess)
			{
				(*index).setStartTime((*index).getArrivalTIme());

				if((*index).getCPU_Burst() - time_quantum <= 0)
				{
					(*index).setEndTime((*index).getArrivalTIme() + (*index).getCPU_Burst());
					(*index).setFinished();
					assignValuesToProcessList(ready_queue.front());
				}
				else
				{
					(*index).setEndTime((*index).getArrivalTIme() + time_quantum);
					(*index).setCPU_Burst((*index).getCPU_Burst() - time_quantum);
					(*index).setProcessed();
					ready_queue.push_back(ready_queue.front());
				}
			}
			else
			{
				(*index).setStartTime(endTimeOfPrevProcess);
				if((*index).getCPU_Burst() - time_quantum <= 0)
				{
					(*index).setEndTime(endTimeOfPrevProcess + (*index).getCPU_Burst());
					(*index).setFinished();
					assignValuesToProcessList(ready_queue.front());
				}
				else
				{
					(*index).setEndTime(endTimeOfPrevProcess + time_quantum);
					(*index).setCPU_Burst((*index).getCPU_Burst() - time_quantum);
					(*index).setProcessed();
					ready_queue.push_back(ready_queue.front());
					
				}


			}

			
			endTimeOfPrevProcess = (*index).getEndTime();
			ready_queue.erase(index);
			
		}

		if(processListItr != processList.end())
		{
			if(processListItr == processList.begin())
				getQueuedProcesses(ready_queue, processListItr, (*processListItr).getArrivalTIme() + time_quantum);
			else
				getQueuedProcesses(ready_queue, processListItr, endTimeOfPrevProcess);
		}
	

		

		index = ready_queue.begin();
		end = ready_queue.end();

	}while(ready_queue.size() != 0);
};

void RR_Scheduler::getQueuedProcesses(vector<Process> &available_queue, vector<Process>::iterator &processListItr, int prevProcEndTime)
{
	int endTime = prevProcEndTime;
	int arrivalTime = 0;

	vector<Process>::iterator posOne, posTwo;	
	
	endTime = prevProcEndTime;
	arrivalTime = (*processListItr).getArrivalTIme();

	while(processListItr != processList.end() && arrivalTime <= endTime)
	{		
		if(arrivalTime <= endTime || available_queue.size() == 0 && (available_queue.front()).getCPU_Burst() <= time_quantum)
			available_queue.push_back(*processListItr);
	
		processListItr++;

		if(processListItr != processList.end())
			arrivalTime = (*processListItr).getArrivalTIme();
	}
}

void RR_Scheduler::assignValuesToProcessList(Process temp)
{
	vector<Process>::iterator location;
	location = processList.begin();
	findProcessID(location, temp.getProcessID());

	(*location).setEndTime(temp.getEndTime());
	(*location).setStartTime(temp.getStartTime());
	(*location).setFinished();

}

void RR_Scheduler::findProcessID(vector<Process>::iterator &location, int processID)
{
	while((*location).getProcessID() != processID)
		location++;
}